/*
 * Copyright (C) 2012 Jonathan St-Michel <jonathan@abiwebz.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.abiwebz.platform.util;

import java.awt.event.ActionListener;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.abiwebz.platform.Application;
import org.abiwebz.platform.util.icons.Theme;
import org.pushingpixels.flamingo.api.common.AbstractCommandButton;
import org.pushingpixels.flamingo.api.common.JCommandButton;
import org.pushingpixels.flamingo.api.ribbon.AbstractRibbonBand;
import org.pushingpixels.flamingo.api.ribbon.JRibbon;
import org.pushingpixels.flamingo.api.ribbon.JRibbonBand;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenu;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryFooter;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntryPrimary;
import org.pushingpixels.flamingo.api.ribbon.RibbonApplicationMenuEntrySecondary;
import org.pushingpixels.flamingo.api.ribbon.RibbonElementPriority;
import org.pushingpixels.flamingo.api.ribbon.RibbonTask;
import org.pushingpixels.flamingo.api.ribbon.resize.CoreRibbonResizePolicies;
import org.pushingpixels.flamingo.api.ribbon.resize.RibbonBandResizePolicy;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * @author stmicj01
 */
public class RibbonManager {

    private final JRibbon ribbon;
    private ArrayList<String> tasks;
    private ArrayList<RibbonAction> bands;
    
    private ArrayList<GroupEntry> groups;

    public RibbonManager(JRibbon ribbon) {
        this.ribbon = ribbon;
        this.tasks = new ArrayList<String>();
        this.bands = new ArrayList<RibbonAction>();
        this.groups = new ArrayList<GroupEntry>();
    }

    public void buildFromXml(String path) {
        buildFromXml(Application.class.getClassLoader().getResourceAsStream(path));
    }

    public void buildFromXml(InputStream istream) {
        try {
            DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(istream);

            doc.getDocumentElement().normalize();

            buildFromXml(doc.getDocumentElement());
        } catch (IOException ex) {
            Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParserConfigurationException ex) {
            Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SAXException ex) {
            Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    public void buildFromXml(Element root) {
        NodeList ribbonList = root.getElementsByTagName("ribbon");
        Node node;
        for (int rid = 0; rid < ribbonList.getLength(); rid++) {
            node = ribbonList.item(rid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                processRibbonElement((Element) node);
            }
        }
    }

    private void processRibbonElement(Element element) {
        Node node;
        Element e;
        NodeList nodes = element.getElementsByTagName("Task");
        String taskname = "";
        for (int tid = 0; tid < nodes.getLength(); tid++) {
            node = nodes.item(tid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                //taskname = ((Element)node).getAttribute("name");
                //addTask(taskname);
                processRibbonTask((Element) node);

            }
        }

        //Créer le Menu principal
        nodes = element.getElementsByTagName("menu");
        for (int mid = 0; mid < nodes.getLength(); mid++) {
            node = nodes.item(mid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                e = (Element) node;
                RibbonApplicationMenu menu = new RibbonApplicationMenu();
                processMenu(e, menu);
                ribbon.setApplicationMenu(menu);
            }
        }

        //Créer la toolbar du rubban
        nodes = element.getElementsByTagName("toolbar");
        for (int tbid = 0; tbid < nodes.getLength(); tbid++) {
            node = nodes.item(tbid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                e = (Element) node; //element toolbar
                processToolbarButton(e);
            }
        }


    }
    
    private void processMenu(Element element, RibbonApplicationMenu menu){
        NodeList nodes = element.getChildNodes();
        Node node;
        Element menuentry;
        String tag;
        for (int bid = 0; bid < nodes.getLength(); bid++) {
            node = nodes.item(bid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                menuentry = (Element) node;
                tag = menuentry.getTagName();
                
                if(tag.equals("separator")) {
                    
                        menu.addMenuSeparator();
                }
                else if(tag.equals("footer")){
                    menu.addFooterEntry(new RibbonApplicationMenuEntryFooter(null, menuentry.getAttribute("name"), null));
                }
                else if(tag.equals("entry")){
                        RibbonApplicationMenuEntryPrimary prim = new RibbonApplicationMenuEntryPrimary(Theme.getInstance(Application.theme).getIconAsResizable(menuentry.getAttribute("icon")), menuentry.getAttribute("name"), null, JCommandButton.CommandButtonKind.POPUP_ONLY);
                        processSecondaryEntries(menuentry, prim);
                        menu.addMenuEntry(prim);
                }
                /*
                switch (tag) {
                    case "separator":
                        menu.addMenuSeparator();
                        break;
                    case "footer":
                        menu.addFooterEntry(new RibbonApplicationMenuEntryFooter(null, menuentry.getAttribute("name"), null));
                        break;
                    case "entry":
                        RibbonApplicationMenuEntryPrimary prim = new RibbonApplicationMenuEntryPrimary(Theme.getInstance(Application.theme).getIconAsResizable(menuentry.getAttribute("icon")), menuentry.getAttribute("name"), null, JCommandButton.CommandButtonKind.POPUP_ONLY);
                        processSecondaryEntries(menuentry, prim);
                        menu.addMenuEntry(prim);
                        break;
                }
                * 
                */
            }
        }
    }
    
    private void processSecondaryEntries(Element element, RibbonApplicationMenuEntryPrimary primary){
        NodeList nodes = element.getChildNodes();
        Node node;
        Element menuentry;
        String tag;
        String group;
        for (int bid = 0; bid < nodes.getLength(); bid++) {
            node = nodes.item(bid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                menuentry = (Element) node;
                group = menuentry.getAttribute("group");
                GroupEntry ge = retrieveGroup(group, primary);
                if(ge == null){
                    ge = new GroupEntry(group, primary);
                    this.groups.add(ge);
                }
                ge.addSecondaryEntry(new RibbonApplicationMenuEntrySecondary(Theme.getInstance(Application.theme).getIconAsResizable(menuentry.getAttribute("icon")), menuentry.getAttribute("name"), createActionListener(menuentry.getAttribute("action")), JCommandButton.CommandButtonKind.ACTION_ONLY));
                //primary.addSecondaryMenuGroup(group, );
                
            }
        }
    }
    
    private GroupEntry retrieveGroup(String group, RibbonApplicationMenuEntryPrimary primary){
        ListIterator<GroupEntry> it = this.groups.listIterator();
        GroupEntry g;
        while(it.hasNext()){
            g = it.next();
            if(g.getGroup().equals(group) && g.getPrimary().equals(primary)){
                return g;
            }
        }
        return null;
    }

    private void processToolbarButton(Element element) {
        NodeList nodes = element.getElementsByTagName("Button");
        Node node;
        Element button;
        for (int bid = 0; bid < nodes.getLength(); bid++) {
            node = nodes.item(bid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                button = (Element) node;
                JCommandButton b = new JCommandButton(button.getAttribute("name"), Theme.getInstance(Application.theme).getIconAsResizable(button.getAttribute("icon")));
                String action = button.getAttribute("action");
                if (action != null) {
                    if (!action.equals("")) {
                        try {
                            try {
                                //Définir l'Action du bouton
                                //b.addActionListener((ActionListener));
                                Object o = ClassLoader.getSystemClassLoader().loadClass(action).newInstance();
                                if (o instanceof ActionListener) {
                                    b.addActionListener((ActionListener) o);
                                }
                            } catch (IllegalAccessException ex) {
                                Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (ClassNotFoundException ex) {
                                Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        } catch (InstantiationException ex) {
                            Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
                ribbon.addTaskbarComponent(b);
            }
        }
    }
    
    private ActionListener createActionListener(String action) {
        if (action != null) {
            if (!action.equals("")) {
                try {
                    try {
                        Object o = ModuleLoader.getInstance().loadClass(action).newInstance();
                        if (o instanceof ActionListener) {
                            return (ActionListener) o;
                        }
                    } catch (IllegalAccessException ex) {
                        Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (ClassNotFoundException ex) {
                        Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                    }
                } catch (InstantiationException ex) {
                    Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return null;
    }

    private void processRibbonTask(Element element) {
        String taskname = element.getAttribute("name");
        addTask(taskname);
        NodeList nodes = element.getElementsByTagName("Band");
        Node node;
        for (int bid = 0; bid < nodes.getLength(); bid++) {
            node = nodes.item(bid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                processRibbonButton((Element) node, taskname);
            }
        }
    }

    private void processRibbonButton(Element element, String taskname) {
        String bandname = element.getAttribute("name");
        NodeList nodes = element.getElementsByTagName("Button");
        Node node;
        Element button;
        for (int bid = 0; bid < nodes.getLength(); bid++) {
            node = nodes.item(bid);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                button = (Element) node;
                JCommandButton b = new JCommandButton(button.getAttribute("name"), Theme.getInstance(Application.theme).getIconAsResizable(button.getAttribute("icon")));
                String action = button.getAttribute("action");
                if (action != null) {
                    if (!action.equals("")) {
                        try {
                            try {
                                //Définir l'Action du bouton
                                //b.addActionListener((ActionListener));
                                
                                Object o = ModuleLoader.getInstance().loadClass(action).newInstance();
                                if (o instanceof ActionListener) {
                                    
                                    b.addActionListener((ActionListener) o);
                                }
                            } catch (IllegalAccessException ex) {
                                Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (ClassNotFoundException ex) {
                                Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        } catch (InstantiationException ex) {
                            Logger.getLogger(RibbonManager.class.getName()).log(Level.SEVERE, null, ex);
                        }
                    }
                }
                RibbonElementPriority p;
                String priority = button.getAttribute("priority");
                if(priority.equals("TOP")){
                    p = RibbonElementPriority.TOP;
                }
                else if(priority.equals("MEDIUM")){
                    p = RibbonElementPriority.MEDIUM;
                }
                else if(priority.equals("LOW")){
                    p = RibbonElementPriority.LOW;
                }
                else{
                    p = RibbonElementPriority.TOP;
                }
                /*
                switch (priority) {
                    case "TOP":
                        p = RibbonElementPriority.TOP;
                        break;
                    case "MEDIUM":
                        p = RibbonElementPriority.MEDIUM;
                        break;
                    case "LOW":
                        p = RibbonElementPriority.LOW;
                        break;
                    default:
                        p = RibbonElementPriority.TOP;
                        break;
                }
                * 
                */
                addComponent(taskname, bandname, b, p);
                //processRibbonButton((Element)node, taskname, node.getNodeName());
            }
        }
    }

    private void addTask(String taskname) {
        if (!this.tasks.contains(taskname)) {
            this.tasks.add(taskname);
        }
    }

    public void addComponent(String taskname, String bandname, JComponent component, RibbonElementPriority priority) {
        addTask(taskname);
        this.bands.add(new RibbonAction(taskname, bandname, component, priority));
    }

    public void generate() {
        ListIterator<String> itTask = this.tasks.listIterator();
        String curTask;
        RibbonTask task;
        while (itTask.hasNext()) {
            curTask = itTask.next();
            AbstractRibbonBand<?>[] taskbands = getBands(curTask);
            task = new RibbonTask(curTask, taskbands);
            this.ribbon.addTask(task);
        }
        
        ListIterator<GroupEntry> itGroup = this.groups.listIterator();
        while(itGroup.hasNext()){
            itGroup.next().generateGroup();
        }
    }

    private JRibbonBand findBand(ArrayList<AbstractRibbonBand<?>> bandlist, String bandname) {
        for (int i = 0; i < bandlist.size(); i++) {
            if (bandlist.get(i).getTitle().toLowerCase().equals(bandname.toLowerCase())) {
                return (JRibbonBand) bandlist.get(i);
            }
        }
        return null;
    }

    private List<RibbonBandResizePolicy> getDefaultPolicies(JRibbonBand band) {
        List<RibbonBandResizePolicy> policies = new ArrayList<RibbonBandResizePolicy>();
        policies.add(new CoreRibbonResizePolicies.Mirror(band.getControlPanel()));
        return policies;
    }

    private AbstractRibbonBand<?>[] getBands(String curTask) {
        ListIterator<RibbonAction> itActions = this.bands.listIterator();
        ArrayList<AbstractRibbonBand<?>> bandlist = new ArrayList<AbstractRibbonBand<?>>();
        RibbonAction a;
        JRibbonBand b;
        int index;
        while (itActions.hasNext()) {
            a = itActions.next();
            if (a.getTaskname().toLowerCase().equals(curTask.toLowerCase())) {
                //Ne pas ajouté là même bande
                b = findBand(bandlist, a.getBandname());
                if (b == null) {
                    b = new JRibbonBand(a.getBandname(), null);
                    b.setResizePolicies(getDefaultPolicies(b));
                    bandlist.add(b);
                }

                if (a.getComponent() instanceof AbstractCommandButton) {
                    b.addCommandButton((AbstractCommandButton) a.getComponent(), a.getPriority());
                } else {
                    b.add(a.getComponent());
                }

            }
        }
        AbstractRibbonBand<?>[] returnList = new AbstractRibbonBand<?>[bandlist.size()];
        for (int i = 0; i < bandlist.size(); i++) {
            returnList[i] = bandlist.get(i);
        }
        return returnList;
    }

    private class RibbonAction {

        private final String bandname;
        private final String taskname;
        private final JComponent component;
        private final RibbonElementPriority priority;

        public RibbonAction(String taskname, String bandname, JComponent component, RibbonElementPriority priority) {
            this.taskname = taskname;
            this.bandname = bandname;
            this.component = component;
            this.priority = priority;
        }

        public String getBandname() {
            return bandname;
        }

        public String getTaskname() {
            return taskname;
        }

        public JComponent getComponent() {
            return component;
        }

        public RibbonElementPriority getPriority() {
            return priority;
        }
    }
    
    private class GroupEntry {
        private final String group;
        private final RibbonApplicationMenuEntryPrimary primary;
        
        private ArrayList<RibbonApplicationMenuEntrySecondary> secondaries;

        public GroupEntry(String group, RibbonApplicationMenuEntryPrimary primary) {
            this.group = group;
            this.primary = primary;
            this.secondaries = new ArrayList<RibbonApplicationMenuEntrySecondary>();
        }
        
        public void addSecondaryEntry(RibbonApplicationMenuEntrySecondary secondary){
            this.secondaries.add(secondary);
        }

        public String getGroup() {
            return group;
        }

        public RibbonApplicationMenuEntryPrimary getPrimary() {
            return primary;
        }
        
        public void generateGroup(){
            RibbonApplicationMenuEntrySecondary[] sec = new RibbonApplicationMenuEntrySecondary[secondaries.size()];
            for(int i = 0; i < secondaries.size(); i++){
                sec[i] = secondaries.get(i);
            }
            this.primary.addSecondaryMenuGroup(this.group, sec);
        }
        
    }
    
}
